package com.flame.shapeville.util;

import com.flame.shapeville.enums.TaskStyle;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Consumer;

/**
 * Utility class providing common helper methods used across the application.
 *
 * @author Flame Dev Team
 * @version 1.0
 * @since 2025-05-08
 */
public class Utils {
    /**
     * Retrieves the last entry from a LinkedHashMap.
     *
     * @param map The input LinkedHashMap
     * @return The last Map.Entry, or null if map is empty or null
     */
    public static Map.Entry<String, Object> getLastNodeOfLinkedHashMap(LinkedHashMap<String, Object> map) {
        if (map == null || map.isEmpty()) {
            return null;
        }
        Map.Entry<String, Object> lastEntry = null;
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            lastEntry = entry;
        }
        return lastEntry;
    }

    /**
     * Retrieves the last key from a Map.
     *
     * @param map The input Map
     * @return The last key as String, or null if map is empty or null
     */
    public static String getLastKeyOfLinkedHashMap(Map<String, Object> map) {
        Map.Entry<String, Object> lastEntry = getLastNodeOfLinkedHashMap((LinkedHashMap<String, Object>) map);
        return lastEntry != null ? lastEntry.getKey() : null;
    }

    /**
     * Checks whether an object is of basic data type.
     *
     * @param obj The object to check
     * @return true if it's a basic data type, false otherwise
     */
    public static boolean isBasicDataType(Object obj) {
        return obj instanceof String || obj instanceof Integer || obj instanceof Long ||
                obj instanceof Double || obj instanceof Float || obj instanceof Boolean;
    }

    /**
     * Gets TaskStyle by its ordinal index.
     *
     * @param index Ordinal index of the TaskStyle enum
     * @return Corresponding TaskStyle or null if not found
     */
    public static TaskStyle getTaskStyle(int index) {
        for (TaskStyle taskStyle : TaskStyle.values()) {
            if (taskStyle.ordinal() == index) {
                return taskStyle;
            }
        }
        return null;
    }

    /**
     * Adds event filter to lose focus when clicking outside a node.
     *
     * @param node       Node to monitor
     * @param onLostFocus Action to perform when focus is lost
     */
    public static void addClickOutsideToLoseFocus(Node node, Consumer<MouseEvent> onLostFocus) {
        Scene scene = node.getScene();
        scene.addEventFilter(MouseEvent.MOUSE_PRESSED, event -> {
            if (!node.getBoundsInParent().contains(event.getSceneX(), event.getSceneY())) {
                onLostFocus.accept(event);
            }
        });
    }

    /**
     * Retrieves a node at specified row and column in a GridPane.
     *
     * @param gridPane GridPane to search
     * @param row Row index
     * @param column Column index
     * @return Found node or null
     */
    public static Node getNodeFromGridPane(GridPane gridPane, int row, int column) {
        for (Node node : gridPane.getChildren()) {
            Integer rowIndex = GridPane.getRowIndex(node);
            Integer columnIndex = GridPane.getColumnIndex(node);

            if (rowIndex == null) rowIndex = 0;
            if (columnIndex == null) columnIndex = 0;

            if (rowIndex == row && columnIndex == column) {
                return node;
            }
        }
        return null;
    }
}
